/* Copyright (c) 2016 Lucky Byte, Inc.
 */
package com.lucky_byte.pay.p071;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.luaj.vm2.LuaValue;

import com.lucky_byte.pay.jar.LuaInterp;
import com.lucky_byte.pay.jar.Runtime;
import com.lucky_byte.pay.jar.lualib.LuaRequest;
import com.lucky_byte.pay.jar.p071.Lib071;
import com.lucky_byte.pay.jar.p071.Packet071;

/**
 * 银联 ACP 后台通知接收服务
 */
public class BackNotifyServlet extends HttpServlet
{
	private static final long serialVersionUID = 4445049065135647728L;
	private static final Logger logger = LogManager.getLogger();

	private File script;

	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		this.script = Runtime.fileFor("/lua/notify/p071/back.lua");
		if (this.script == null || !this.script.exists()) {
			logger.fatal("文件[{}]不存在，安装不完整，不能提供服务.",
					this.script.getAbsolutePath());
		}
		Runtime.setNotifySender("ACP 后台通知服务");

		logger.trace("启动 P071 Back Notify 服务...");
	}

	@Override
	public void destroy() {
		logger.trace("停止 P071 Back Notify 服务 ...");
	}

	@Override
	protected void doGet(HttpServletRequest reqt, HttpServletResponse resp)
			throws IOException, ServletException {
		logger.error("客户尝试通过 GET 方法访问本服务，这是不允许的.");
		resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
	}

	@Override
	protected void doPost(HttpServletRequest reqt, HttpServletResponse resp)
			throws ServletException, IOException {
		String encoding = reqt.getCharacterEncoding();
		if (encoding == null) {
			String content_type = reqt.getContentType();
			if (content_type != null) {
				String[] strv = content_type.split(";");
				for (String str : strv) {
					str = str.trim();
					if (str.toLowerCase().startsWith("charset=")) {
						encoding = str.substring("charset=".length());
					}
				}
			}
		}
		if (encoding == null) {
			encoding = "UTF-8";
		}
		StringBuilder builder = new StringBuilder();
		BufferedReader reader = reqt.getReader();
		while (true) {
			String line = reader.readLine();
			if (line == null) {
				break;
			}
			builder.append(line);
		}
		String data = URLDecoder.decode(builder.toString(), encoding);

		logger.info("接收银联 ACP 后台通知报文[{}].", data);

		// 解析接收到的数据，构造报文对象
		Packet071 packet = new Packet071();
		if (!packet.parse(data)) {
			logger.error("解析来自[{}]的响应报文错误.", reqt.getRemoteAddr());
			resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
			resp.getWriter().close();
			return;
		}
		String merno = packet.get("merId");
		if (merno == null) {
			logger.error("后台通知报文中缺少[merId]字段.");
			resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
			resp.getWriter().close();
			return;
		}
		// 验证响应报文签名
		String vers_key_name = "acp_vers_" + merno;
		if (!Lib071.verifySign(packet, vers_key_name)) {
			logger.error("验证后台通知报文签名失败.");
			resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
			resp.getWriter().close();
			return;
		}
		logger.info("验证 ACP 通知报文签名通过...");

		if (!this.doScript(reqt, resp, packet)) {
			resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		} else {
			resp.setStatus(HttpServletResponse.SC_OK);
		}
		resp.getWriter().close();
	}

	/**
	 * 通过脚本处理业务流程
	 */
	private boolean doScript(HttpServletRequest reqt,
			HttpServletResponse resp, Packet071 packet) throws IOException {
		LuaInterp interp = new LuaInterp();
		interp.load(new LuaRequest(packet));
		LuaValue result = interp.eval(this.script);
		if (result.isboolean() && !result.checkboolean()) {
			logger.error("处理 ACP 后台通知错误.");
			return false;
		}
		return true;
	}
}
